home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / vol_400 / 422_02 / dosutil / grep.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-03-20  |  15.6 KB  |  580 lines

  1. /*
  2.  * The  information  in  this  document  is  subject  to  change
  3.  * without  notice  and  should not be construed as a commitment
  4.  * by Digital Equipment Corporation or by DECUS.
  5.  *
  6.  * Neither Digital Equipment Corporation, DECUS, nor the authors
  7.  * assume any responsibility for the use or reliability of  this
  8.  * document or the described software.
  9.  *
  10.  *      Copyright (C) 1980, DECUS
  11.  *
  12.  * General permission to copy or modify, but not for profit,  is
  13.  * hereby  granted,  provided that the above copyright notice is
  14.  * included and reference made to  the  fact  that  reproduction
  15.  * privileges were granted by DECUS.
  16.  *
  17.  * Compile command: cc grep -fop
  18.  */
  19.  
  20. #include <stdio.h>
  21.  
  22. /*
  23.  * grep.
  24.  *
  25.  * Runs on the Decus compiler or on vms.
  26.  *
  27.  * On vms, define as:
  28.  *
  29.  *      grep :== "$disk:[account]grep"      (native)
  30.  *      grep :== "$disk:[account]grep grep" (Decus)
  31.  *
  32.  * See below for more information.
  33.  */
  34.  
  35. char    *documentation[] = {
  36. "grep searches a file for a given pattern.  Execute by",
  37. "   grep [flags] regular_expression file_list",
  38. "",
  39. "Flags are single characters preceeded by '-':",
  40. "   -c      Only a count of matching lines is printed",
  41. "   -f      Print file name for matching lines switch, see below",
  42. "   -n      Each line is preceeded by its line number",
  43. "   -v      Only print non-matching lines",
  44. "",
  45. "The file_list is a list of files (wildcards are acceptable on RSX modes).",
  46.  
  47. "",
  48. "The file name is normally printed if there is a file given.",
  49. "The -f flag reverses this action (print name no file, not if more).",
  50. "",
  51. 0 };
  52.  
  53. char    *patdoc[] = {
  54. "The regular_expression defines the pattern to search for.  Upper- and",
  55. "lower-case are always ignored.  Blank lines never match.  The expression",
  56. "should be quoted to prevent file-name translation.",
  57. "x      An ordinary character (not mentioned below) matches that character.",
  58. "'\\'    The backslash quotes any character.  \"\\$\" matches a dollar-sign.",
  59. "'^'    A circumflex at the beginning of an expression matches the",
  60. "       beginning of a line.",
  61. "'$'    A dollar-sign at the end of an expression matches the end of a line.",
  62. "'.'    A period matches any character except \"new-line\".",
  63. "':a'   A colon matches a class of characters described by the following",
  64. "':d'     character.  \":a\" matches any alphabetic, \":d\" matches digits,",
  65. "':n'     \":n\" matches alphanumerics, \": \" matches spaces, tabs, and",
  66. "': '     other control characters, such as new-line.",
  67. "'*'    An expression followed by an asterisk matches zero or more",
  68. "       occurrances of that expression: \"fo*\" matches \"f\", \"fo\"",
  69. "       \"foo\", etc.",
  70. "'+'    An expression followed by a plus sign matches one or more",
  71. "       occurrances of that expression: \"fo+\" matches \"fo\", etc.",
  72. "'-'    An expression followed by a minus sign optionally matches",
  73. "       the expression.",
  74. "'[]'   A string enclosed in square brackets matches any character in",
  75. "       that string, but no others.  If the first character in the",
  76. "       string is a circumflex, the expression matches any character",
  77. "       except \"new-line\" and the characters in the string.  For",
  78. "       example, \"[xyz]\" matches \"xx\" and \"zyx\", while \"[^xyz]\"",
  79. "       matches \"abc\" but not \"axb\".  A range of characters may be",
  80. "       specified by two characters separated by \"-\".  Note that,",
  81. "       [a-z] matches alphabetics, while [z-a] never matches.",
  82. "The concatenation of regular expressions is a regular expression.",
  83. 0};
  84.  
  85. #define LMAX    512
  86. #define PMAX    256
  87.  
  88. #define CHAR    1
  89. #define BOL     2
  90. #define EOL     3
  91. #define ANY     4
  92. #define CLASS   5
  93. #define NCLASS  6
  94. #define STAR    7
  95. #define PLUS    8
  96. #define MINUS   9
  97. #define ALPHA   10
  98. #define DIGIT   11
  99. #define NALPHA  12
  100. #define PUNCT   13
  101. #define RANGE   14
  102. #define ENDPAT  15
  103.  
  104. int cflag=0, fflag=0, nflag=0, vflag=0, nfile=0, debug=0;
  105.  
  106. char *pp, lbuf[LMAX], pbuf[PMAX];
  107.  
  108. extern char *cclass(), *pmatch();
  109.  
  110.  
  111. /*** Main program - parse arguments & grep *************/
  112. main(argc, argv)
  113. int argc;
  114. char *argv[];
  115. {
  116.    register char   *p;
  117.    register int    c, i;
  118.    int             gotpattern;
  119.  
  120.    FILE            *f;
  121.  
  122.    if (argc <= 1)
  123.       usage("No arguments");
  124.    if (argc == 2 && argv[1][0] == '?' && argv[1][1] == 0) {
  125.       help(documentation);
  126.       help(patdoc);
  127.       return;
  128.       }
  129.    nfile = argc-1;
  130.    gotpattern = 0;
  131.    for (i=1; i < argc; ++i) {
  132.       p = argv[i];
  133.       if (*p == '-') {
  134.          ++p;
  135.          while (c = *p++) {
  136.             switch(tolower(c)) {
  137.  
  138.             case '?':
  139.                help(documentation);
  140.                break;
  141.  
  142.             case 'C':
  143.             case 'c':
  144.                ++cflag;
  145.                break;
  146.  
  147.             case 'D':
  148.             case 'd':
  149.                ++debug;
  150.                break;
  151.  
  152.             case 'F':
  153.             case 'f':
  154.                ++fflag;
  155.                break;
  156.  
  157.             case 'n':
  158.             case 'N':
  159.                ++nflag;
  160.                break;
  161.  
  162.             case 'v':
  163.             case 'V':
  164.                ++vflag;
  165.                break;
  166.  
  167.             default:
  168.                usage("Unknown flag");
  169.             }
  170.          }
  171.          argv[i] = 0;
  172.          --nfile;
  173.       } else if (!gotpattern) {
  174.          compile(p);
  175.          argv[i] = 0;
  176.          ++gotpattern;
  177.          --nfile;
  178.       }
  179.    }
  180.    if (!gotpattern)
  181.       usage("No pattern");
  182.    if (nfile == 0)
  183.       grep(stdin, 0);
  184.    else {
  185.       fflag = fflag ^ (nfile > 0);
  186.       for (i=1; i < argc; ++i) {
  187.          if (p = argv[i]) {
  188.             if ((f=fopen(p, "r")) == NULL)
  189.                cant(p);
  190.             else {
  191.                grep(f, p);
  192.                fclose(f);
  193.             }
  194.          }
  195.       }
  196.    }
  197. }
  198.  
  199. /*** Display a file name *******************************/
  200. file(s)
  201. char *s;
  202. {
  203.    printf("File %s:\n", s);
  204. }
  205.  
  206. /*** Report unopenable file ****************************/
  207. cant(s)
  208. char *s;
  209. {
  210.    fprintf(stderr, "%s: cannot open\n", s);
  211. }
  212.  
  213. /*** Give good help ************************************/
  214. help(hp)
  215. char **hp;
  216. {
  217.    register char   **dp;
  218.  
  219.    for (dp = hp; *dp; ++dp)
  220.       printf("%s\n", *dp);
  221. }
  222.  
  223. /*** Display usage summary *****************************/
  224. usage(s)
  225. char    *s;
  226. {
  227.    fprintf(stderr, "?GREP-E-%s\n", s);
  228.    fprintf(stderr,
  229.       "Usage: grep [-cfnv] pattern [file ...].  grep ? for help\n");
  230.    exit(1);
  231. }
  232.  
  233. /*** Compile the pattern into global pbuf[] ************/
  234. compile(source)
  235. char       *source;   /* Pattern to compile */
  236. {
  237.    register char  *s;         /* Source string pointer     */
  238.    register char  *lp;        /* Last pattern pointer      */
  239.    register int   c;          /* Current character         */
  240.    int            o;          /* Temp                      */
  241.    char           *spp;       /* Save beginning of pattern */
  242.  
  243.    s = source;
  244.    if (debug)
  245.       printf("Pattern = \"%s\"\n", s);
  246.    pp = pbuf;
  247.    while (c = *s++) {
  248.       /*
  249.        * STAR, PLUS and MINUS are special.
  250.        */
  251.       if (c == '*' || c == '+' || c == '-') {
  252.          if (pp == pbuf ||
  253.               (o=pp[-1]) == BOL ||
  254.               o == EOL ||
  255.               o == STAR ||
  256.               o == PLUS ||
  257.               o == MINUS)
  258.             badpat("Illegal occurrance op.", source, s);
  259.          store(ENDPAT);
  260.          store(ENDPAT);
  261.          spp = pp;               /* Save pattern end     */
  262.          while (--pp > lp)       /* Move pattern down    */
  263.             *pp = pp[-1];        /* one byte             */
  264.          *pp =   (c == '*') ? STAR :
  265.             (c == '-') ? MINUS : PLUS;
  266.          pp = spp;               /* Restore pattern end  */
  267.          continue;
  268.       }
  269.       /*
  270.        * All the rest.
  271.        */
  272.       lp = pp;         /* Remember start       */
  273.       switch(c) {
  274.  
  275.       case '^':
  276.          store(BOL);
  277.          break;
  278.  
  279.       case '$':
  280.          store(EOL);
  281.          break;
  282.  
  283.       case '.':
  284.          store(ANY);
  285.          break;
  286.  
  287.       case '[':
  288.          s = cclass(source, s);
  289.          break;
  290.  
  291.       case ':':
  292.          if (*s) {
  293.             switch(tolower(c = *s++)) {
  294.  
  295.             case 'a':
  296.             case 'A':
  297.                store(ALPHA);
  298.                break;
  299.  
  300.             case 'd':
  301.             case 'D':
  302.                store(DIGIT);
  303.                break;
  304.  
  305.             case 'n':
  306.             case 'N':
  307.                store(NALPHA);
  308.                break;
  309.  
  310.             case ' ':
  311.                store(PUNCT);
  312.                break;
  313.  
  314.             default:
  315.                badpat("Unknown : type", source, s);
  316.  
  317.             }
  318.             break;
  319.          }
  320.          else    badpat("No : type", source, s);
  321.  
  322.       case '\\':
  323.          if (*s)
  324.             c = *s++;
  325.  
  326.       default:
  327.          store(CHAR);
  328.          store(tolower(c));
  329.       }
  330.    }
  331.    store(ENDPAT);
  332.    store(0);                /* Terminate string     */
  333.    if (debug) {
  334.       for (lp = pbuf; lp < pp;) {
  335.          if ((c = (*lp++ & 0377)) < ' ')
  336.             printf("\\%o ", c);
  337.          else    printf("%c ", c);
  338.         }
  339.         printf("\n");
  340.    }
  341. }
  342.  
  343. /*** Compile a class (within []) ***********************/
  344. char *cclass(source, src)
  345. char       *source;   /* Pattern start -- for error msg. */
  346. char       *src;      /* Class start */
  347. {
  348.    register char   *s;        /* Source pointer    */
  349.    register char   *cp;       /* Pattern start     */
  350.    register int    c;         /* Current character */
  351.    int             o;         /* Temp              */
  352.  
  353.    s = src;
  354.    o = CLASS;
  355.    if (*s == '^') {
  356.       ++s;
  357.       o = NCLASS;
  358.    }
  359.    store(o);
  360.    cp = pp;
  361.    store(0);                          /* Byte count      */
  362.    while ((c = *s++) && c!=']') {
  363.       if (c == '\\') {                /* Store quoted char    */
  364.          if ((c = *s++) == '\0')      /* Gotta get something  */
  365.             badpat("Class terminates badly", source, s);
  366.          else    store(tolower(c));
  367.       }
  368.       else if (c == '-' &&
  369.             (pp - cp) > 1 && *s != ']' && *s != '\0') {
  370.          c = pp[-1];             /* Range start     */
  371.          pp[-1] = RANGE;         /* Range signal    */
  372.          store(c);               /* Re-store start  */
  373.          c = *s++;               /* Get end char and*/
  374.          store(tolower(c));      /* Store it        */
  375.       }
  376.       else {
  377.          store(tolower(c));      /* Store normal char */
  378.       }
  379.    }
  380.    if (c != ']')
  381.       badpat("Unterminated class", source, s);
  382.    if ((c = (pp - cp)) >= 256)
  383.       badpat("Class too large", source, s);
  384.    if (c == 0)
  385.       badpat("Empty class", source, s);
  386.    *cp = c;
  387.    return(s);
  388. }
  389.  
  390. /*** Store an entry in the pattern buffer **************/
  391. store(op)
  392.    int op;
  393. {
  394.    if (pp >= &pbuf[PMAX])
  395.       error("Pattern too complex\n");
  396.    *pp++ = op;
  397. }
  398.  
  399. /*** Report a bad pattern specification ****************/
  400. badpat(message, source, stop)
  401. char  *message;       /* Error message */
  402. char  *source;        /* Pattern start */
  403. char  *stop;          /* Pattern end   */
  404. {
  405.    fprintf(stderr, "-GREP-E-%s, pattern is\"%s\"\n", message, source);
  406.    fprintf(stderr, "-GREP-E-Stopped at byte %d, '%c'\n",
  407.          stop-source, stop[-1]);
  408.    error("?GREP-E-Bad pattern\n");
  409. }
  410.  
  411. /*** Scan the file for the pattern in pbuf[] ***********/
  412. grep(fp, fn)
  413. FILE       *fp;       /* File to process            */
  414. char       *fn;       /* File name (for -f option)  */
  415. {
  416.    register int lno, count, m;
  417.  
  418.    lno = 0;
  419.    count = 0;
  420.    while (fgets(lbuf, LMAX, fp)) {
  421.       ++lno;
  422.       m = match();
  423.       if ((m && !vflag) || (!m && vflag)) {
  424.          ++count;
  425.          if (!cflag) {
  426.             if (fflag && fn) {
  427.                file(fn);
  428.                fn = 0;
  429.             }
  430.             if (nflag)
  431.                printf("%d\t", lno);
  432.             printf("%s\n", lbuf);
  433.          }
  434.       }
  435.    }
  436.    if (cflag) {
  437.       if (fflag && fn)
  438.          file(fn);
  439.       printf("%d\n", count);
  440.    }
  441. }
  442.  
  443. /*** Match line (lbuf) with pattern (pbuf) return 1 if match ***/
  444. match()
  445. {
  446.    register char   *l;        /* Line pointer       */
  447.  
  448.    for (l = lbuf; *l; ++l) {
  449.       if (pmatch(l, pbuf))
  450.          return(1);
  451.    }
  452.    return(0);
  453. }
  454.  
  455. /*** Match partial line with pattern *******************/
  456. char *pmatch(line, pattern)
  457. char               *line;     /* (partial) line to match      */
  458. char               *pattern;  /* (partial) pattern to match   */
  459. {
  460.    register char   *l;        /* Current line pointer         */
  461.    register char   *p;        /* Current pattern pointer      */
  462.    register char   c;         /* Current character            */
  463.    char            *e;        /* End for STAR and PLUS match  */
  464.    int             op;        /* Pattern operation            */
  465.    int             n;         /* Class counter                */
  466.    char            *are;      /* Start of STAR match          */
  467.  
  468.    l = line;
  469.    if (debug > 1)
  470.       printf("pmatch(\"%s\")\n", line);
  471.    p = pattern;
  472.    while ((op = *p++) != ENDPAT) {
  473.       if (debug > 1)
  474.          printf("byte[%d] = 0%o, '%c', op = 0%o\n",
  475.                l-line, *l, *l, op);
  476.       switch(op) {
  477.  
  478.       case CHAR:
  479.          if (tolower(*l++) != *p++)
  480.             return(0);
  481.          break;
  482.  
  483.       case BOL:
  484.          if (l != lbuf)
  485.             return(0);
  486.          break;
  487.  
  488.       case EOL:
  489.          if (*l != '\0')
  490.             return(0);
  491.          break;
  492.  
  493.       case ANY:
  494.          if (*l++ == '\0')
  495.             return(0);
  496.          break;
  497.  
  498.       case DIGIT:
  499.          if ((c = *l++) < '0' || (c > '9'))
  500.             return(0);
  501.          break;
  502.  
  503.       case ALPHA:
  504.          c = tolower(*l++);
  505.          if (c < 'a' || c > 'z')
  506.             return(0);
  507.          break;
  508.  
  509.       case NALPHA:
  510.          c = tolower(*l++);
  511.          if (c >= 'a' && c <= 'z')
  512.             break;
  513.          else if (c < '0' || c > '9')
  514.             return(0);
  515.          break;
  516.  
  517.       case PUNCT:
  518.          c = *l++;
  519.          if (c == 0 || c > ' ')
  520.             return(0);
  521.          break;
  522.  
  523.       case CLASS:
  524.       case NCLASS:
  525.          c = tolower(*l++);
  526.          n = *p++ & 0377;
  527.          do {
  528.             if (*p == RANGE) {
  529.                p += 3;
  530.                n -= 2;
  531.                if (c >= p[-2] && c <= p[-1])
  532.                   break;
  533.             }
  534.             else if (c == *p++)
  535.                break;
  536.          } while (--n > 1);
  537.          if ((op == CLASS) == (n <= 1))
  538.             return(0);
  539.          if (op == CLASS)
  540.             p += n - 2;
  541.          break;
  542.  
  543.       case MINUS:
  544.          e = pmatch(l, p);       /* Look for a match    */
  545.          while (*p++ != ENDPAT); /* Skip over pattern   */
  546.          if (e)                  /* Got a match?        */
  547.             l = e;               /* Yes, update string  */
  548.          break;                  /* Always succeeds     */
  549.  
  550.       case PLUS:                 /* One or more ...     */
  551.          if ((l = pmatch(l, p)) == 0)
  552.             return(0);           /* Gotta have a match  */
  553.       case STAR:                 /* Zero or more ...    */
  554.          are = l;                /* Remember line start */
  555.          while (*l && (e = pmatch(l, p)))
  556.             l = e;               /* Get longest match   */
  557.          while (*p++ != ENDPAT); /* Skip over pattern   */
  558.          while (l >= are) {      /* Try to match rest   */
  559.             if (e = pmatch(l, p))
  560.                return(e);
  561.             --l;                 /* Nope, try earlier   */
  562.          }
  563.          return(0);              /* Nothing else worked */
  564.  
  565.       default:
  566.          printf("Bad op code %d\n", op);
  567.          error("Cannot happen -- match\n");
  568.       }
  569.    }
  570.    return(l);
  571. }
  572.  
  573. /*** Report an error ***********************************/
  574. error(s)
  575. char *s;
  576. {
  577.    fprintf(stderr, "%s", s);
  578.    exit(1);
  579. }
  580.